/*
 * Tranquil Java Integrated Development Environment
 *
 * The GNU General Public License Version 3
 *
 * Copyright (C) 2021 Autumn Lamonte
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @author Autumn Lamonte [AutumnWalksTheLake@gmail.com] ⚧ Trans Liberation Now
 * @version 1
 */
package tjide.ui;

import java.text.MessageFormat;
import java.util.ResourceBundle;

import gjexer.TApplication;
import gjexer.TWindow;
import gjexer.bits.CellAttributes;
import gjexer.bits.StringUtils;
import gjexer.event.TKeypressEvent;
import gjexer.event.TMenuEvent;
import gjexer.event.TMouseEvent;
import gjexer.menu.TMenu;
import static gjexer.TKeypress.*;

import tjide.build.CompileListener;
import tjide.build.CompileTask;
import tjide.project.Target;

/**
 * CompileStatusWindow is a permanent modal-looking (but not actually modal)
 * window that reports the progress of a compile or make operation.
 */
public class CompileStatusWindow extends TWindow implements CompileListener {

    /**
     * Translated strings.
     */
    private static ResourceBundle i18n = ResourceBundle.getBundle(CompileStatusWindow.class.getName());

    // ------------------------------------------------------------------------
    // Constants --------------------------------------------------------------
    // ------------------------------------------------------------------------


    // ------------------------------------------------------------------------
    // Variables --------------------------------------------------------------
    // ------------------------------------------------------------------------

    /**
     * The number of kilobytes available for the compile job.
     */
    private long availableMemory = 0;

    /**
     * The number of errors encountered.
     */
    private int errorCount = 0;

    /**
     * The number of warnings encountered.
     */
    private int warningCount = 0;

    /**
     * The number of lines processed.
     */
    private int lineCount = 0;

    /**
     * The total number of errors encountered.
     */
    private int totalErrorCount = 0;

    /**
     * The total number of warnings encountered.
     */
    private int totalWarningCount = 0;

    /**
     * The total number of lines processed.
     */
    private int totalLineCount = 0;

    /**
     * If true, the compile has completed and failed.
     */
    private boolean compileFailed = false;

    /**
     * If true, the compile has completed and succeeded.
     */
    private boolean compileSucceeded = false;

    /**
     * The source filename being compiled.
     */
    private String sourceFilename = null;

    /**
     * The destination filename being compiled.
     */
    private String destinationFilename = null;

    /**
     * The compile task, so that we can call cancel().
     */
    private CompileTask compileTask = null;

    // ------------------------------------------------------------------------
    // Constructors -----------------------------------------------------------
    // ------------------------------------------------------------------------

    /**
     * Construct window.  It will be centered on screen.
     *
     * @param parent the main application
     */
    public CompileStatusWindow(final TApplication parent) {
        super(parent, i18n.getString("windowTitle"), 0, 0, 60, 13,
            CENTERED | NOCLOSEBOX | NOZOOMBOX | OVERRIDEMENU);

        addShortcutKeypress(kbCtrlC);

        // Start hidden.
        hide();
    }

    // ------------------------------------------------------------------------
    // Event handlers ---------------------------------------------------------
    // ------------------------------------------------------------------------

    /**
     * Handle mouse press events.
     *
     * @param mouse mouse button press event
     */
    @Override
    public void onMouseDown(final TMouseEvent mouse) {
        if (compileTask != null) {
            if (compileTask.isCompileRunning()) {
                // Do nothing, compile is still in progress.
                return;
            }

            // Compile is finished, user just closed the window.  Put the
            // messages window on top.
            TranquilApplication app = (TranquilApplication) getApplication();
            app.getMessageWindow().activate();
        }

        // No compile running, hide the window.
        hide();
    }

    /**
     * Handle keystrokes.
     *
     * @param keypress keystroke event
     */
    @Override
    public void onKeypress(final TKeypressEvent keypress) {
        // Ctrl-C - cancel
        if (keypress.equals(kbCtrlC)) {
            if (compileTask != null) {
                compileTask.cancelCompile();
            }
            return;
        }

        if (compileTask != null) {
            if (compileTask.isCompileRunning()) {
                // Do nothing, compile is still in progress.
                return;
            }

            // Compile is finished, user just closed the window.  Put the
            // messages window on top.
            TranquilApplication app = (TranquilApplication) getApplication();
            app.getMessageWindow().activate();
        }

        // No compile running, hide the window.
        hide();
    }

    // ------------------------------------------------------------------------
    // TWindow behavior -------------------------------------------------------
    // ------------------------------------------------------------------------

    /**
     * Called by TApplication.drawChildren() to render on screen.
     */
    @Override
    public void draw() {
        super.draw();
        CellAttributes color = getTheme().getColor("compileStatusWindow.labels");

        putStringXY(2, 2, i18n.getString("mainFile"), color);
        if (sourceFilename != null) {
            if (((TranquilApplication) getApplication()).getProject() == null) {
                putStringXY(13, 2, MessageFormat.
                    format(i18n.getString("sourceToDest"), i18n.getString("EDITOR"),
                        sourceFilename), color);
            } else {
                putStringXY(13, 2, sourceFilename, color);
            }
        }
        putStringXY(2, 3, i18n.getString("compiling"), color);
        if (destinationFilename != null) {
            putStringXY(13, 3, destinationFilename, color);
        }
        putStringXY(21, 5, i18n.getString("total"), color);
        putStringXY(30, 5, i18n.getString("file"), color);
        putStringXY(5, 6, i18n.getString("linesCompiled"), color);
        putStringXY(11, 7, i18n.getString("warnings"), color);
        putStringXY(13, 8, i18n.getString("errors"), color);
        putStringXY(3, 10, i18n.getString("availableMemory"), color);
        putStringXY(21, 10, String.format("%dK", availableMemory),
            color);

        putStringXY(21, 6, String.format("%d", totalLineCount + lineCount),
            color);
        putStringXY(30, 6, String.format("%d", lineCount), color);
        putStringXY(21, 7, String.format("%d",
                totalWarningCount + warningCount), color);
        putStringXY(30, 7, String.format("%d", warningCount), color);
        putStringXY(21, 8, String.format("%d", totalErrorCount + errorCount),
            color);
        putStringXY(30, 8, String.format("%d", errorCount), color);

        CellAttributes statusLabelColor = getTheme().getColor("compileStatusWindow.statusLabel");
        hLineXY(1, 11, getWidth() - 2, ' ', statusLabelColor);
        CellAttributes statusTextColor = getTheme().getColor("compileStatusWindow.statusText");

        if (compileTask == null) {
            putStringXY(3, 11, i18n.getString("noCompileRunning"),
                statusLabelColor);
            putCharXY(19, 11, ':', statusLabelColor);
            putStringXY(23, 11, i18n.getString("pressAnyKey"),
                statusTextColor);
        } else {
            if (compileFailed) {
                putStringXY(3, 11, i18n.getString("compileFailed"),
                    statusLabelColor);
                putCharXY(19, 11, ':', statusLabelColor);
                putStringXY(23, 11, i18n.getString("pressAnyKey"),
                    statusTextColor);
            } else if (compileSucceeded) {
                putStringXY(3, 11, i18n.getString("compileSuccess"),
                    statusLabelColor);
                putCharXY(19, 11, ':', statusLabelColor);
                putStringXY(23, 11, i18n.getString("pressAnyKey"),
                    statusTextColor);
            } else {
                String message = i18n.getString("compileInProgress");
                putStringXY((getWidth() - message.length()) / 2, 11, message,
                    statusLabelColor);
            }
        }

    }

    /**
     * Retrieve the background color.
     *
     * @return the background color
     */
    @Override
    public final CellAttributes getBackground() {
        if (inWindowMove) {
            assert (isActive());
            return getTheme().getColor("twindow.background.modal");
        } else if (isActive()) {
            return getTheme().getColor("twindow.background.modal");
        } else {
            return getTheme().getColor("twindow.background.modal.inactive");
        }
    }

    /**
     * Retrieve the border color.
     *
     * @return the border color
     */
    @Override
    public CellAttributes getBorder() {
        if (inWindowMove) {
            assert (isActive());
            return getTheme().getColor("twindow.border.modal.windowmove");
        } else if (isActive()) {
            return getTheme().getColor("twindow.border.modal");
        } else {
            return getTheme().getColor("twindow.border.modal.inactive");
        }
    }

    /**
     * Called by application.switchWindow() when another window gets the
     * focus.
     */
    @Override
    public void onUnfocus() {
        hide();
    }

    // ------------------------------------------------------------------------
    // CompileListener --------------------------------------------------------
    // ------------------------------------------------------------------------

    /**
     * Notify of a new warning message.  This will increment the warning
     * count.
     *
     * @param target the target that generated this message
     * @param line the line number of the message.  1-based: 0 or negative
     * means no relevant line number.
     * @param column the column number of the message.  1-based: 0 or
     * negative means no relevant column number.
     * @param message the message text
     */
    public void addCompileWarning(final Target target, final long line,
        final long column, final String message) {

        warningCount++;

        TranquilApplication app = (TranquilApplication) getApplication();
        app.getMessageWindow().addCompileWarning(target, line, column, message);
        getApplication().postEvent(new TMenuEvent(TMenu.MID_REPAINT));
    }

    /**
     * Notify of a new error message.  This will increment the error count.
     *
     * @param target the target that generated this message
     * @param line the line number of the message.  1-based: 0 or negative
     * means no relevant line number.
     * @param column the column number of the message.  1-based: 0 or
     * negative means no relevant column number.
     * @param message the message text
     */
    public void addCompileError(final Target target, final long line,
        final long column, final String message) {

        errorCount++;

        TranquilApplication app = (TranquilApplication) getApplication();
        app.getMessageWindow().addCompileError(target, line, column, message);

        getApplication().postEvent(new TMenuEvent(TMenu.MID_REPAINT));
    }

    /**
     * Notify a new error count.
     *
     * @param count the new count of errors
     */
    public void setCompileErrorCount(final int count) {
        errorCount = count;
        getApplication().postEvent(new TMenuEvent(TMenu.MID_REPAINT));
    }

    /**
     * Notify a new warning count.
     *
     * @param count the new count of warnings
     */
    public void setCompileWarningCount(final int count) {
        warningCount = count;
        getApplication().postEvent(new TMenuEvent(TMenu.MID_REPAINT));
    }

    /**
     * Notify a new line count.
     *
     * @param count the new count of lines
     */
    public void setCompileLineCount(final int count) {
        lineCount += count;
        getApplication().postEvent(new TMenuEvent(TMenu.MID_REPAINT));
    }

    /**
     * Notify a new available memory number.
     *
     * @param kbytes the number of kilobytes available
     */
    public void setCompileAvailableMemory(final long kbytes) {
        availableMemory = kbytes;
        getApplication().postEvent(new TMenuEvent(TMenu.MID_REPAINT));
    }

    /**
     * Notify of a compile beginning.
     */
    public void setCompileBegin() {
        compileFailed           = false;
        compileSucceeded        = false;
        errorCount              = 0;
        warningCount            = 0;
        lineCount               = 0;
        totalErrorCount         = 0;
        totalWarningCount       = 0;
        totalLineCount          = 0;
        availableMemory         = 0;
        sourceFilename          = null;
        destinationFilename     = null;

        TranquilApplication app = (TranquilApplication) getApplication();
        app.getMessageWindow().clearMessages();
        app.getMessageWindow().activate();

        activate();
    }

    /**
     * Add the per-file metrics to the total.
     */
    private void addFileMetrics() {
        totalLineCount          += lineCount;
        totalWarningCount       += warningCount;
        totalErrorCount         += errorCount;
        getApplication().postEvent(new TMenuEvent(TMenu.MID_REPAINT));
    }

    /**
     * Reset the per-file metrics for the next file.
     */
    private void resetFileMetrics() {
        errorCount              = 0;
        warningCount            = 0;
        lineCount               = 0;
        getApplication().postEvent(new TMenuEvent(TMenu.MID_REPAINT));
    }

    /**
     * Notify of a compile failure.
     *
     * @param completed if true, this is the final failure message
     */
    public void setCompileFailed(final boolean completed) {
        if (completed) {
            compileFailed = true;
            compileSucceeded = false;
        }

        TranquilApplication app = (TranquilApplication) getApplication();
        if (!app.getMessageWindow().isShown()) {
            app.getMessageWindow().activate();
        }
        if (!isShown()) {
            activate();
        }

        if (completed) {
            app.getMessageWindow().addCompileFailedSummary(null,
                totalErrorCount + errorCount, totalWarningCount + warningCount,
                totalLineCount + lineCount);
        } else {
            app.getMessageWindow().addCompileFailedSummary(compileTask.getTarget(),
                errorCount, warningCount, lineCount);
        }
     }

    /**
     * Notify of a compile success.
     *
     * @param completed if true, this is the final success message
     */
    public void setCompileSucceeded(final boolean completed) {
        if (completed) {
            compileFailed = false;
            compileSucceeded = true;
        }

        TranquilApplication app = (TranquilApplication) getApplication();
        if (!app.getMessageWindow().isShown()) {
            app.getMessageWindow().activate();
        }
        if (!isShown()) {
            activate();
        }

        if (completed) {
            app.getMessageWindow().addCompileSuccessSummary(null,
                totalErrorCount + errorCount, totalWarningCount + warningCount,
                totalLineCount + lineCount);
        } else {
            app.getMessageWindow().addCompileSuccessSummary(compileTask.getTarget(),
                errorCount, warningCount, lineCount);
        }
    }

    /**
     * Notify of a file beginning the compile.
     *
     * @param source the source filename being compiled
     * @param destination the destination filename being compiled
     */
    public void setCompileFile(final String source, final String destination) {
        sourceFilename = source;
        destinationFilename = destination;
        addFileMetrics();
        resetFileMetrics();
    }

    /**
     * Set the compile task callback interface, so that the CompileListener
     * can cancel if needed.
     *
     * @param compileTask the compile task
     */
    public void setCompileTask(final CompileTask compileTask) {
        this.compileTask = compileTask;
    }

}
